package permission

import (
	"HeadLineNews/pkg/db/store"
	"HeadLineNews/pkg/snow"
	"HeadLineNews/pkg/zaplog"
	"HeadLineNews/proto_models/common"
	models "HeadLineNews/proto_models/manage/permission_model"
	"HeadLineNews/resp"
	"HeadLineNews/utils/gin_ctx"
	"HeadLineNews/utils/transaction"
	context2 "context"
	"database/sql"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/jmoiron/sqlx"
	"strings"
)

var (
	errGroupHasExists = errors.New("该群组已存在")

	errGroupNotExists = errors.New("不存在的群组")
	errPermNotExists  = errors.New("不存在的权限")
	errPermHasInGroup = errors.New("权限已在群组内")

	errAdminNotExists  = errors.New("不存在的管理员")
	errAdminHasInGroup = errors.New("管理员已在群组内")
)

type Repo interface {
	CheckUniqueGroup(c *gin.Context, groupName string) (err error)
	InsertGroup(c *gin.Context, p *models.RequestAddGroup) (err error)
	DeleteGroup(c *gin.Context) (err error)
	ListGroup(c *gin.Context, p *common.PageSize) (data *models.ResponseListGroup, err error)
	ListPerms(c *gin.Context, p *common.PageSize) (data *models.ResponseListPerms, err error)

	CheckGroupExists(c *gin.Context, groupId string) (err error)

	CheckPermsExist(c *gin.Context, permIds ...string) (err error)
	CheckPermHasInGroup(c *gin.Context, p *models.RequestAddPermToGroup) (err error)
	InsertPermsToGroup(c *gin.Context, p *models.RequestAddPermToGroup) (err error)
	UpdatePermsToGroup(c *gin.Context, p *models.RequestUpdatePermToGroup) (err error)
	ListGroupPerms(c *gin.Context, p *models.RequestListGroupPerms) (data *models.ResponseGroupPerms, err error)

	CheckAdminsExist(c *gin.Context, adminIds ...string) (err error)
	CheckAdminHasInGroup(c *gin.Context, p *models.RequestAddAdminToGroup) (err error)
	InsertAdminsToGroup(c *gin.Context, p *models.RequestAddAdminToGroup) (err error)
	UpdateAdminsToGroup(c *gin.Context, p *models.RequestUpdateAdminToGroup) (err error)
	ListGroupAdmins(c *gin.Context, p *models.RequestListGroupAdmins) (data *models.ResponseGroupAdmins, err error)

	FlushGroupPerms(c *gin.Context) (err error)
}

type repo struct {
	db *sqlx.DB
}

func NewRepo() Repo {
	return &repo{db: store.DB()}
}

// CheckUniqueGroup 检查群组名称唯一
func (r *repo) CheckUniqueGroup(c *gin.Context, groupName string) (err error) {
	sqlStr := `SELECT COUNT(*) FROM manage__group WHERE group_name=?`
	var count int64
	if err = r.db.Get(&count, sqlStr, groupName); err != nil {
		zaplog.L().Error("query group by groupName failed", zaplog.Fields(c, groupName, err)...)
		resp.ServerBusy(c)
		return
	}
	if count > 0 {
		err = errGroupHasExists
		zaplog.L().Warn("group has exists", zaplog.Fields(c, groupName, nil)...)
		resp.UnprovedParam(c, err.Error())
		return
	}
	return
}

// InsertGroup 将权限保存到数据库
func (r *repo) InsertGroup(c *gin.Context, p *models.RequestAddGroup) (err error) {
	sqlStr := `INSERT INTO manage__group(group_id, group_name, create_by, 
                          last_update_by, create_time, last_update_time)VALUES(?,?,?,?,NOW(),NOW()) `
	if _, err = r.db.Exec(sqlStr,
		snow.GetID(),
		p.GroupName,
		c.GetString(gin_ctx.UserId),
		c.GetString(gin_ctx.UserId),
	); err != nil {
		zaplog.L().Error("insert group failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// DeleteGroup 删除群组
func (r *repo) DeleteGroup(c *gin.Context) (err error) {
	groupId := c.Param("groupId")
	sqlStr := `DELETE FROM manage__group WHERE group_id=? `
	var res sql.Result
	if res, err = r.db.Exec(sqlStr, groupId); err != nil {
		zaplog.L().Error("delete group by id failed", zaplog.Fields(c, groupId, err)...)
		resp.ServerBusy(c)
		return
	}
	if row, _ := res.RowsAffected(); row == 0 {
		err = errGroupNotExists
		zaplog.L().Warn("no group deleted by id", zaplog.Fields(c, groupId, err)...)
		resp.UnprovedParam(c, err.Error())
		return
	}
	return
}

// ListGroup 批量返回群组数据
func (r repo) ListGroup(c *gin.Context, p *common.PageSize) (data *models.ResponseListGroup, err error) {
	data = new(models.ResponseListGroup)
	var records = make([]*models.AGroup, 0, p.Size)
	sqlStr := `SELECT g.group_id, g.group_name, g.create_time, a.admin_name AS create_by 
       		   FROM manage__group g, manage__admin a 
       		   WHERE g.create_by=a.admin_id
       		   LIMIT ? OFFSET ?`
	if err = r.db.Select(&records, sqlStr, p.Size, (p.Page-1)*p.Size); err != nil {
		zaplog.L().Error("query groups failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	if len(records) == 0 {
		return
	}
	var count int64
	sqlStr = `SELECT COUNT(*) FROM manage__group`
	if err = r.db.Get(&count, sqlStr); err != nil {
		zaplog.L().Error("query group count failed", zaplog.Fields(c, nil, err)...)
		resp.ServerBusy(c)
		return
	}
	data.Count = count
	data.Records = records
	return
}

// ListPerms 展示权限
func (r *repo) ListPerms(c *gin.Context, p *common.PageSize) (data *models.ResponseListPerms, err error) {
	data = new(models.ResponseListPerms)
	perms := make([]*models.APerm, 0, p.Size)
	sqlStr := `SELECT perm_id, perm_url, perm_comment FROM manage__permission LIMIT ? OFFSET ?`
	if err = r.db.Select(&perms, sqlStr, p.Size, (p.Page-1)*p.Size); err != nil {
		zaplog.L().Error("query perms failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	sqlStr = `SELECT COUNT(*) FROM manage__permission`
	var count int64
	if err = r.db.Get(&count, sqlStr); err != nil {
		zaplog.L().Error("query perms count failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	data.Count = count
	data.Perms = perms
	return
}

// CheckGroupExists 检查群组是否合法
func (r *repo) CheckGroupExists(c *gin.Context, groupId string) (err error) {
	sqlStr := `SELECT COUNT(*) FROM manage__group WHERE group_id=?`
	var count int64
	if err = r.db.Get(&count, sqlStr, groupId); err != nil {
		zaplog.L().Error("query group by id failed", zaplog.Fields(c, groupId, err)...)
		resp.ServerBusy(c)
		return
	}
	if count == 0 {
		err = errGroupNotExists
		zaplog.L().Warn("invalid group id", zaplog.Fields(c, groupId, nil)...)
		resp.UnprovedParam(c, err.Error())
	}
	return
}

// CheckPermsExist 检查被添加的权限id是否全部合法
func (r *repo) CheckPermsExist(c *gin.Context, permIds ...string) (err error) {
	sqlStr := `SELECT COUNT(*) FROM manage__permission WHERE FIND_IN_SET(perm_id, ?)`
	var count int
	if err = r.db.Get(&count, sqlStr, strings.Join(permIds, ",")); err != nil {
		zaplog.L().Error("query perms failed", zaplog.Fields(c, permIds, err)...)
		resp.ServerBusy(c)
		return
	}
	if count != len(permIds) {
		err = errPermNotExists
		zaplog.L().Warn("invalid perm", zaplog.Fields(c, permIds, nil)...)
		resp.UnprovedParam(c, err.Error())
		return
	}
	return
}

// CheckPermHasInGroup 检查被添加的权限是否已经在群组内（群组权限联合唯一）
func (r *repo) CheckPermHasInGroup(c *gin.Context, p *models.RequestAddPermToGroup) (err error) {
	var groupPermIds []string
	sqlStr := `SELECT perm_id FROM manage__group_perm WHERE group_id=?`
	if err = r.db.Select(&groupPermIds, sqlStr, p.GroupId); err != nil {
		zaplog.L().Error("query perms by groupId failed", zaplog.Fields(c, p.GroupId, err)...)
		resp.ServerBusy(c)
		return
	}
	for _, permId := range groupPermIds {
		for _, urlId := range p.UrlIds {
			if permId == urlId { // 只要存在相同的路由id
				err = errPermHasInGroup
				zaplog.L().Warn("perm has in group", zaplog.Fields(c, urlId, nil)...)
				resp.UnprovedParam(c, err.Error())
				return
			}
		}
	}
	return
}

// InsertPermsToGroup 将群组权限保存到数据库
func (r *repo) InsertPermsToGroup(c *gin.Context, p *models.RequestAddPermToGroup) (err error) {
	var groupPerms []map[string]interface{}
	for _, permId := range p.UrlIds {
		groupPerms = append(groupPerms, map[string]interface{}{
			"group_id":       p.GroupId,
			"perm_id":        permId,
			"create_by":      c.GetString(gin_ctx.UserId),
			"last_update_by": c.GetString(gin_ctx.UserId),
		})
	}

	sqlStr := `INSERT INTO manage__group_perm(
            group_id, perm_id, create_by, last_update_by, create_time, last_update_time) 
			VALUES (:group_id, :perm_id, :create_by,:last_update_by, NOW(), NOW())`
	if _, err = r.db.NamedExec(sqlStr, groupPerms); err != nil {
		zaplog.L().Error("insert group perms failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// UpdatePermsToGroup 更新群组权限
func (r *repo) UpdatePermsToGroup(c *gin.Context, p *models.RequestUpdatePermToGroup) (err error) {
	if err = transaction.Transaction(r.db, func(tx *sqlx.Tx) (te error) {
		sqlStr := `DELETE FROM manage__group_perm WHERE group_id=?`
		if _, te = tx.Exec(sqlStr, p.GroupId); te != nil {
			zaplog.L().Error("delete group perms by groupId failed", zaplog.Fields(c, p.GroupId, te)...)
			return
		}
		var groupPerms []map[string]interface{}
		for _, permId := range p.UrlIds {
			groupPerms = append(groupPerms, map[string]interface{}{
				"group_id":       p.GroupId,
				"perm_id":        permId,
				"create_by":      c.GetString(gin_ctx.UserId),
				"last_update_by": c.GetString(gin_ctx.UserId),
			})
		}

		sqlStr = `INSERT INTO manage__group_perm(
            group_id, perm_id, create_by, last_update_by, create_time, last_update_time) 
			VALUES (:group_id, :perm_id, :create_by,:last_update_by, NOW(), NOW())`
		if _, te = tx.NamedExec(sqlStr, groupPerms); err != nil {
			zaplog.L().Error("update group perms failed", zaplog.Fields(c, p, te)...)
			resp.ServerBusy(c)
			return
		}
		return
	}); err != nil {
		return
	}
	return
}

// ListGroupPerms 查看群组有哪些权限
func (r *repo) ListGroupPerms(c *gin.Context, p *models.RequestListGroupPerms) (data *models.ResponseGroupPerms, err error) {
	data = new(models.ResponseGroupPerms)
	var perms []*models.APerm
	sqlStr := `SELECT p.perm_id, p.perm_url, p.perm_comment
			FROM manage__group_perm gp, manage__permission p 
			WHERE gp.perm_id=p.perm_id
			AND gp.group_id=? LIMIT ? OFFSET ?`
	if err = r.db.Select(&perms, sqlStr, p.GroupId, p.PageSize.Size, (p.PageSize.Page-1)*p.PageSize.Size); err != nil {
		zaplog.L().Error("query group perms by groupId failed",
			zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	if len(perms) == 0 {
		return
	}
	var count int64
	sqlStr = `SELECT COUNT(*) FROM manage__group_perm WHERE group_id=?`
	if err = r.db.Get(&count, sqlStr, p.GroupId); err != nil {
		zaplog.L().Error("query group perms count by groupId failed", zaplog.Fields(c, p.GroupId, err)...)
		resp.ServerBusy(c)
		return
	}
	data.Count = count
	data.GroupPerms = perms
	return
}

// CheckAdminsExist 检查被添加的adminId是否全部合法
func (r *repo) CheckAdminsExist(c *gin.Context, adminIds ...string) (err error) {
	sqlStr := `SELECT COUNT(*) FROM manage__admin WHERE FIND_IN_SET(admin_id, ?)`
	var count int
	if err = r.db.Get(&count, sqlStr, strings.Join(adminIds, ",")); err != nil {
		zaplog.L().Error("query admins failed", zaplog.Fields(c, adminIds, err)...)
		resp.ServerBusy(c)
		return
	}
	if count != len(adminIds) {
		err = errAdminNotExists
		zaplog.L().Warn("invalid adminId", zaplog.Fields(c, adminIds, nil)...)
		resp.UnprovedParam(c, err.Error())
		return
	}
	return
}

// CheckAdminHasInGroup 检查被添加的管理员是否已经在群组内（群组管理员联合唯一）
func (r *repo) CheckAdminHasInGroup(c *gin.Context, p *models.RequestAddAdminToGroup) (err error) {
	var groupAdminIds []string
	sqlStr := `SELECT admin_id FROM manage__group_admin WHERE group_id=?`
	if err = r.db.Select(&groupAdminIds, sqlStr, p.GroupId); err != nil {
		zaplog.L().Error("query admins by groupId failed", zaplog.Fields(c, p.GroupId, err)...)
		resp.ServerBusy(c)
		return
	}
	for _, adminId := range groupAdminIds {
		for _, aId := range p.AdminIds {
			if adminId == aId { // 只要存在相同的adminId
				err = errAdminHasInGroup
				zaplog.L().Warn("admin has in group", zaplog.Fields(c, adminId, nil)...)
				resp.UnprovedParam(c, err.Error())
				return
			}
		}
	}
	return
}

// InsertAdminsToGroup 将群组管理员保存到数据库
func (r *repo) InsertAdminsToGroup(c *gin.Context, p *models.RequestAddAdminToGroup) (err error) {
	var groupAdmins []map[string]interface{}
	for _, adminId := range p.AdminIds {
		groupAdmins = append(groupAdmins, map[string]interface{}{
			"group_id":       p.GroupId,
			"admin_id":       adminId,
			"create_by":      c.GetString(gin_ctx.UserId),
			"last_update_by": c.GetString(gin_ctx.UserId),
		})
	}

	sqlStr := `INSERT INTO manage__group_admin(
            group_id, admin_id, create_by, last_update_by, create_time, last_update_time) 
			VALUES (:group_id, :admin_id, :create_by,:last_update_by, NOW(), NOW())`
	if _, err = r.db.NamedExec(sqlStr, groupAdmins); err != nil {
		zaplog.L().Error("insert group admins failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// UpdateAdminsToGroup 更新群组管理员
func (r *repo) UpdateAdminsToGroup(c *gin.Context, p *models.RequestUpdateAdminToGroup) (err error) {
	if err = transaction.Transaction(r.db, func(tx *sqlx.Tx) (te error) {
		sqlStr := `DELETE FROM manage__group_admin WHERE group_id=?`
		if _, te = tx.Exec(sqlStr, p.GroupId); te != nil {
			zaplog.L().Error("delete group admins by groupId failed", zaplog.Fields(c, p.GroupId, te)...)
			return
		}
		var groupAdmins []map[string]interface{}
		for _, adminId := range p.AdminIds {
			groupAdmins = append(groupAdmins, map[string]interface{}{
				"group_id":       p.GroupId,
				"admin_id":       adminId,
				"create_by":      c.GetString(gin_ctx.UserId),
				"last_update_by": c.GetString(gin_ctx.UserId),
			})
		}

		sqlStr = `INSERT INTO manage__group_admin(
            group_id, admin_id, create_by, last_update_by, create_time, last_update_time) 
			VALUES (:group_id, :admin_id, :create_by,:last_update_by, NOW(), NOW())`
		if _, err = tx.NamedExec(sqlStr, groupAdmins); err != nil {
			zaplog.L().Error("update group admins failed", zaplog.Fields(c, p, err)...)
			resp.ServerBusy(c)
			return
		}
		return
	}); err != nil {
		return
	}
	return
}

// ListGroupAdmins 查看群组有哪些管理员
func (r *repo) ListGroupAdmins(c *gin.Context, p *models.RequestListGroupAdmins) (data *models.ResponseGroupAdmins, err error) {
	data = new(models.ResponseGroupAdmins)
	var admins []*models.AGroupAdmin
	sqlStr := `SELECT a.admin_id, a.admin_name
			FROM manage__group_admin ga, manage__admin a 
			WHERE ga.admin_id=a.admin_id
			AND ga.group_id=? LIMIT ? OFFSET ?`
	if err = r.db.Select(&admins, sqlStr, p.GroupId, p.PageSize.Size, (p.PageSize.Page-1)*p.PageSize.Size); err != nil {
		zaplog.L().Error("query group admins by groupId failed",
			zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	if len(admins) == 0 {
		return
	}
	var count int64
	sqlStr = `SELECT COUNT(*) FROM manage__group_admin  WHERE group_id=?`
	if err = r.db.Get(&count, sqlStr, p.GroupId); err != nil {
		zaplog.L().Error("query group perms count by groupId failed", zaplog.Fields(c, p.GroupId, err)...)
		resp.ServerBusy(c)
		return
	}
	data.Count = count
	data.GroupAdmins = admins
	return
}

// FlushGroupPerms 将权限刷新 redis
func (r *repo) FlushGroupPerms(c *gin.Context) (err error) {
	type groupPerm struct {
		GroupId string `db:"group_id"`
		PermUrl string `db:"perm_url"`
	}
	var allGroupPerms []*groupPerm
	//查询出所有群组的权限信息
	sqlStr := `SELECT gp.group_id, p.perm_url
			FROM manage__group_perm gp, manage__permission p 
			WHERE gp.perm_id=p.perm_id`
	if err = r.db.Select(&allGroupPerms, sqlStr); err != nil {
		zaplog.L().Error("query group perms failed", zaplog.Fields(c, nil, err)...)
		resp.ServerBusy(c)
		return
	}
	// 将权限刷新进 redis
	tx := NewCache().cli.TxPipeline()
	defer func() { _ = tx.Close() }()
	ct := context2.Background()

	tx.FlushDB(ct) // 清空当前权限库
	for _, gp := range allGroupPerms {
		tx.SAdd(ct, gp.GroupId, gp.PermUrl)
	}
	if _, err = tx.Exec(ct); err != nil {
		_ = tx.Discard()
		zaplog.L().Error("flush perms to redis failed", zaplog.Fields(c, nil, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}
